/*
  Name: GLRagdoll.cpp
  Copyright:
  Author: ilija
  Date: 07.06.07 23:13
  Description: for game only!
*/


#include "GLRagdoll.h"


static const char *RD_PART_NAMES[RP_Count] = {
    "root",
    "pelvis",
    "leftleg1",
    "leftleg2",
    "leftfoot",
    "rightleg1",
    "rightleg2",
    "rightfoot",
    "torso1",
    "torso2",
    "leftarm1",
    "leftarm2",
    "lefthand",
    "rightarm1",
    "rightarm2",
    "righthand",
    "neck",
    "head"
};


//
// RagdollFrame
//

const RagdollFrame& RagdollFrame::operator =(const RagdollFrame &frm)
{
    for (int i = 0; i < RP_Count; i++)
    {
        rot[i] = frm.rot[i];
    }
    //memcpy(&rot, &(frm.rot), RP_Count * sizeof(rot));
    return *this;
}


//
// RagdollAnimation
//

RagdollAnimation::RagdollAnimation()
{
    anim_frames_count = 0;
    anim_speed = 1.0f;
}

bool RagdollAnimation::loadAnimation(const char* sfile, float speed, GLRagdoll* doll)
{
    TiXmlDocument doc(sfile);
	if (doc.LoadFile())
	{
		TiXmlElement* root = doc.FirstChildElement("ragdoll");
		parseXmlNode(root, NULL, doll);
		anim_speed = speed;
		return true;
	}
    return false;
}

void RagdollAnimation::parseXmlNode(TiXmlElement* elem, RagdollFrame* frame, GLRagdoll* doll)
{
    RagdollFrame* next_frame = frame;
    if (strcmp(elem->Value(), "ragdoll") == 0)
    {
        const char *sframes = elem->Attribute("frames");
        if (sframes)
            anim_frames_count = atoi(sframes);
    }
    /*else if (strcmp(elem->Value(), "item") == 0 && doll)
    {
        float px,py,pz,rx,ry,rz,ss;
        int part = GLRagdoll::getPartByName(elem->Attribute("part"));
        sscanf(elem->Attribute("pos"), "%f %f %f", &px, &py, &pz);
        sscanf(elem->Attribute("rot"), "%f %f %f", &rx, &ry, &rz);
        sscanf(elem->Attribute("scale"), "%f", &ss);
        doll->getBodyPart(part)->createObject()
            ->setModel(doll->getSceneGraph()->getEngine()->getModel(elem->Attribute("model")))
            ->setPosition(px, py, pz)
            ->setRotation(rx, ry, rz)
            ->setScale(ss);
    }*/
    else if (strcmp(elem->Value(), "frame") == 0)
    {
        const char *sindex = elem->Attribute("index");
        if (sindex)
            next_frame = &anim_frames[atoi(sindex)];
    }
    else if (frame)
    {
        int part = GLRagdoll::getPartByName(elem->Value());
        float x,y,z;
        if (part > -1)
        {
            const char *srot = elem->Attribute("rot");
            sscanf(srot, "%f %f %f", &x, &y, &z);
            frame->rot[part].set(x, y, z);
        }
    }
	//recursion
	TiXmlElement* child = elem->FirstChildElement();
	while (child)
	{
		parseXmlNode(child, next_frame, doll);
		child = child->NextSiblingElement();
	}
}

void RagdollAnimation::saveAnimation(const char *filename, GLRagdoll* doll)
{
    FILE *fp = fopen(filename, "wt");
    if (fp)
    {
        fprintf(fp, "<ragdoll version=\"1\" frames=\"%i\">\n", anim_frames_count);
        /*if (doll)
        {
            fprintf(fp, "\t<project>\n");
            for (int i = 0; i < RP_Count; i++)
            {
                if (doll->getBodyPart(i)->getNext())
                {
                    GLObject* obj = doll->getBodyPart(i)->getNext();
                    fprintf(fp, "\t\t<item part=\"%s\" model=\"%s\" pos=\"%f %f %f\" rot=\"%f %f %f\" scale=\"%f\" />\n",
                            RD_PART_NAMES[i],
                            obj->model->getFileName().c_str(),
                            obj->position.x, obj->position.y, obj->position.z,
                            obj->rotation.x, obj->rotation.y, obj->rotation.z,
                            obj->scale);
                }
            }
            fprintf(fp, "\t</project>\n");
        }*/
        for (int i = 0; i < anim_frames_count; i++)
        {
            fprintf(fp, "\t<frame index=\"%i\">\n", i);
            for (int j = 0; j < RP_Count; j++)
            {
                fprintf(fp, "\t\t<%s rot=\"%f %f %f\" />\n",
                        RD_PART_NAMES[j],
                        anim_frames[i].rot[j].x,
                        anim_frames[i].rot[j].y,
                        anim_frames[i].rot[j].z);
            }
            fprintf(fp, "\t</frame>\n");
        }
        fprintf(fp, "</ragdoll>");
        fclose(fp);
    }
}

void RagdollAnimation::resetAnimation()
{
    for (int i = 0; i < RD_MAXFRAMES; i++)
    {
        for (int j = 0; j < RP_Count; j++)
        {
            anim_frames[i].rot[j].reset();
        }
    }
    anim_frames_count = 0;
    anim_speed = 1.0f;
}


//
// GLRagdoll
//

//static
int GLRagdoll::getPartByName(const char* name)
{
    for (int i = 0; i < RP_Count; i++)
    {
        if (strcmp(name, RD_PART_NAMES[i]) == 0)
            return i;
    }
    return -1;
}


//konstruktor
GLRagdoll::GLRagdoll(GLObject* obj) :
    GLObject(obj)
{
    float bw = 0.08;

    //skelet
    initPart(RP_Root,      0,           vector3f(0, 0, 0), vector3f(0, 0, 0), vector3f(0, 0, 0));
    initPart(RP_Pelvis,    RP_Root,     vector3f(0, 0, 0.82), vector3f(0.16, 0.1, 0.12), vector3f(0, 0, 0));
    initPart(RP_LeftLeg1,  RP_Pelvis,   vector3f(-0.1, 0, 0), vector3f(bw, bw, 0.4), vector3f(0, 0, -0.2));
    initPart(RP_LeftLeg2,  RP_LeftLeg1, vector3f(0, 0, -0.4), vector3f(bw, bw, 0.4), vector3f(0, 0, -0.2));
    initPart(RP_LeftFoot,  RP_LeftLeg2, vector3f(0, 0, -0.4), vector3f(0.1, 0.2, 0.05), vector3f(0, 0.05, 0));
    initPart(RP_RightLeg1, RP_Pelvis,   vector3f(0.1, 0, 0), vector3f(bw, bw, 0.4), vector3f(0, 0, -0.2));
    initPart(RP_RightLeg2, RP_RightLeg1,vector3f(0, 0, -0.4), vector3f(bw, bw, 0.4), vector3f(0, 0, -0.2));
    initPart(RP_RightFoot, RP_RightLeg2,vector3f(0, 0, -0.4), vector3f(0.1, 0.2, 0.05), vector3f(0, 0.05, 0));
    initPart(RP_Torso1,    RP_Pelvis,   vector3f(0, 0, 0), vector3f(0.2, 0.12, 0.3), vector3f(0, 0, 0.15));
    initPart(RP_Torso2,    RP_Torso1,   vector3f(0, 0, 0.3), vector3f(0.3, 0.13, 0.2), vector3f(0, 0, 0.1));
    initPart(RP_LeftArm1,  RP_Torso2,   vector3f(-0.18, 0, 0.2), vector3f(bw, bw, 0.3), vector3f(0, 0, -0.15));
    initPart(RP_LeftArm2,  RP_LeftArm1, vector3f(0, 0, -0.3), vector3f(bw, bw, 0.3), vector3f(0, 0, -0.15));
    initPart(RP_LeftHand,  RP_LeftArm2, vector3f(0, 0, -0.3), vector3f(0.06, 0.11, 0.1), vector3f(0, 0, -0.05));
    initPart(RP_RightArm1, RP_Torso2,   vector3f(0.18, 0, 0.2), vector3f(bw, bw, 0.3), vector3f(0, 0, -0.15));
    initPart(RP_RightArm2, RP_RightArm1,vector3f(0, 0, -0.3), vector3f(bw, bw, 0.3), vector3f(0, 0, -0.15));
    initPart(RP_RightHand, RP_RightArm2,vector3f(0, 0, -0.3), vector3f(0.06, 0.11, 0.1), vector3f(0, 0, -0.05));
    initPart(RP_Neck,      RP_Torso2,   vector3f(0, 0, 0.2), vector3f(bw, bw, 0.05), vector3f(0, 0, 0.025));
    initPart(RP_Head,      RP_Neck,     vector3f(0, 0, 0.05), vector3f(0.2, 0.2, 0.2), vector3f(0, 0, 0.1));

    animation = NULL;
    curFramePos = 0.0f;

    for (int i = 0; i < RP_Count; i++)
    {
        bodyPart[i]->setColor(Color(255, 200, 14));
        if (i > 0)
            bodyPart[i]->render_box = true;
    }
}


void GLRagdoll::setAnimation(RagdollAnimation* anim)
{
    animation = anim;
    if (anim == NULL)
        return;
    curFrame = animation->anim_frames[0];
    curFramePos = 0;
    applyCurrentFrame();
}


void GLRagdoll::clearAnim()
{
	for (int i = 0; i < RP_Count; i++)
    {
		curFrame.rot[i].reset();
    }
    applyCurrentFrame();
}

void GLRagdoll::initPart(int part, int prev_part, vector3f offset, vector3f bsize, vector3f bcenter)
{
    if (prev_part == part)
        bodyPart[part] = this->createObject();
    else
        bodyPart[part] = bodyPart[prev_part]->createObject();
    bodyPart[part]->position = offset;
    bodyPart[part]->setBoxSize(bsize);
    bodyPart[part]->setBoxCenter(bcenter);
}

void GLRagdoll::applyCurrentFrame()
{
    for (int i = 0; i < RP_Count; i++)
    {
        if (i == 0)
            bodyPart[i]->position = curFrame.rot[i];
        else
            bodyPart[i]->rotation = curFrame.rot[i];
    }
}

void GLRagdoll::calcFrame(float f)
{
    if (animation == NULL)
        return;
    int f1 = (int)f;
    int f2 = f1 + 1;
    if (f2 == animation->anim_frames_count) f2 = 0;
    float ff = f - f1;
    RagdollFrame *rf1 = &animation->anim_frames[f1];
    RagdollFrame *rf2 = &animation->anim_frames[f2];
    for (int i = 0; i < RP_Count; i++)
    {
        curFrame.rot[i].x = rf1->rot[i].x + (rf2->rot[i].x - rf1->rot[i].x) * ff;
        curFrame.rot[i].y = rf1->rot[i].y + (rf2->rot[i].y - rf1->rot[i].y) * ff;
        curFrame.rot[i].z = rf1->rot[i].z + (rf2->rot[i].z - rf1->rot[i].z) * ff;
    }
    applyCurrentFrame();
}

bool GLRagdoll::updateAnim(float dt, bool loop)
{
    if (animation == NULL)
        return false;

    if (animation->anim_frames_count == 0)
        return true;

    bool b = false;

    curFramePos += dt * animation->anim_speed;
	if (loop)
	{
		while (curFramePos > animation->anim_frames_count)
		{
			curFramePos -= animation->anim_frames_count;
			//ako prodje celu animaciju
			b = true;
		}
	}
	else
	{
		if (curFramePos > animation->anim_frames_count - 1)
		{
			curFramePos = animation->anim_frames_count - 1;
			b = true;
		}
	}

    calcFrame(curFramePos);
    return b;
}


void GLRagdoll::rotatePart(int part, float x, float y, float z, bool set)
{
    if (set)
    {
        curFrame.rot[part].x = x;
        curFrame.rot[part].y = y;
        curFrame.rot[part].z = z;
    }
    else
    {
        curFrame.rot[part].x += x;
        curFrame.rot[part].y += y;
        curFrame.rot[part].z += z;
    }
    applyCurrentFrame();
}

